<?php
/**
 * Created by PhpStorm.
 * User: Inhere
 * Date: 2015/2/7
 * Time: 21:28
 * Note: app directory path manager
 * File: PathManager.php
 */
namespace ulue\core\utils;

use ulue\core\abstracts\AbstractCore;
use ulue\libs\datastorage\AbstractDataOperator;

class PathManager extends AbstractDataOperator
{

    /**
     * @var string
     */
    protected $basePath;

    /**
     * @param null $basePath
     * @param array $data
     */
    public function __construct($basePath=null, array $data=[] )
    {
        $this->setBasePath($basePath);

        if ($data) {
            $this->mset($data);

            foreach ($data as $key => $value) {
                $this->lock($key);
            }
        }
    }

    public function path($key)
    {
        return $this->resolvePath($key);
    }

    public function getPath($key)
    {
        return $this->resolvePath($key);
    }

    /**
     * 获取项目目录的完整路径,结尾无 /
     * @param $key
     * @return mixed|null|string
     */
    public function resolvePath($key)
    {
        $key = trim($key);

        if ( $key{0} ==='@') {
            return $this->resolveAlias($key);
        }

        if ( $this->exists($key) ) {
            $dir = $this->get($key);

            return is_dir($dir) ? $dir : $this->basePath . '/'. $dir;
        }

        return $key;
    }

    public function dir($key, $value=null)
    {
        if ($value) {
            $this->addDir($key, $value);
        } else {
            return $this->getDir($key);
        }
    }

    /**
     * @param $key
     * @param $value
     * @return $this
     */
    public function addDir($key, $value)
    {
        if ( !$this->exists($key) ) {
            $this->set($key,rtrim(trim($value),'\\/') );
        }

        return $this;
    }

    /**
     * 获取项目的目录名称，相对于项目目录 PROJECT_PATH (defined at ulue/core/define.php)
     * @param $key
     * @return null|string
     */
    public function getDir($key)
    {
        $key = trim($key);

        if ( $this->exists($key) ) {
            $dir = $this->get($key);

            if ( strpos($dir,'/')!==false ) {
                return trim( strrchr($dir,'/'), '/' );
            }

            return trim($dir);
        }

        return $key;
    }

    /**
     * Sets the value of basePath.
     *
     * @param string $basePath the base path
     *
     * @return self
     */
    protected function setBasePath($basePath)
    {
        if ( file_exists($basePath) ) {
            $this->basePath = $basePath;
        }

        return $this;
    }

    /**
     * set/get basePath
     * @param null $path
     * @return null|string
     */
    public function basePath($path=null)
    {
        if ( file_exists($path) ) {
            $this->basePath = $path;
        }

        return $this->basePath;
    }

    /**
     * state description lock free
     * @param string $alias 别名
     * @param string $path 对应的真实路径
     * @throws \DRuntimeException
     * @return $this
     */
    public function alias($alias, $path='')
    {
        if ( !$path || !is_string($path) ) {
            throw new \InvalidArgumentException(sprintf(
                'The 2th parameter must be of type string is not empty, %s given',
                gettype($path)
            ));
        }

        if ( $alias{0} != '@' ) {
            throw new \DRuntimeException( '别名首字符必须设置为 @ 符号' );
        }

        if ( $this->isLockedAlias($alias) ) {
            throw new \DRuntimeException(sprintf('别名：%s , 已被锁定,不可更改。请设置其他名称。',$alias));
        }

        if ( !isset($this->_aliases[$alias]) ) {
            $this->_aliases[$alias] = rtrim( str_replace( '\\' , '/', trim($path)) , '/ ');
        }

        return $this;
    }

    /**
     * @param $alias
     * @return mixed|string
     */
    public function resolveAlias($alias)
    {
        if ( !$alias || !is_string($alias) )
            return null;

        $alias = rtrim( trim($alias), '\\/');

        #常规路径直接返回
        if ($alias{0} != '@' && file_exists($alias)) {
            return $alias;
        }

        if ( isset($this->_aliases[$alias]) ) {
            return $this->_aliases[$alias];
        }

        $hasExt = false;
        $pos = strrpos($alias,'.');
        $ext = substr($alias, $pos );

        if ( in_array($ext, $this->supportedExt()) ) {
            $hasExt = true;
            $alias = substr($alias, 0, $pos );
        }

        $path = str_replace(['\\','.'], '/', $alias);

        if ( strpos($path, '/')===false ) {
            return $path;
        }

        # 路径别名 $pathAlias @example: @app --> /app
        list($pathAlias, $other) = explode('/', $path,2);

        if ( $aliasPath = $this->isAlias($pathAlias) )
        {
            $realPath   = $aliasPath.'/'.$other;
            $realPath   = $hasExt ? $realPath . $ext : $realPath;

            return $this->_aliases[$alias]= $realPath;
        }

        return $hasExt ? $path . $ext : $path;
    }

    public function supportedExt()
    {
        return ['.js', '.json', '.css', '.php', '.yml', '.inc', '.ini', '.html', '.twig'];
    }

    public function __get($name)
    {
        if ( $path = $this->getPath($name) ) {
            return $path;
        }

        return null;
    }

}